<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title>The source code</title>
  <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
  <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
  <style type="text/css">
    .highlight { display: block; background-color: #ddd; }
  </style>
  <script type="text/javascript">
    function highlight() {
      document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
    }
  </script>
</head>
<body onload="prettyPrint(); highlight();">
  <pre class="prettyprint lang-js"><span id='Ext-DomHelper'>/**
</span> * @class Ext.DomHelper
 * &lt;p&gt;The DomHelper class provides a layer of abstraction from DOM and transparently supports creating
 * elements via DOM or using HTML fragments. It also has the ability to create HTML fragment templates
 * from your DOM building code.&lt;/p&gt;
 *
 * &lt;p&gt;&lt;b&gt;&lt;u&gt;DomHelper element specification object&lt;/u&gt;&lt;/b&gt;&lt;/p&gt;
 * &lt;p&gt;A specification object is used when creating elements. Attributes of this object
 * are assumed to be element attributes, except for 4 special attributes:
 * &lt;div class=&quot;mdetail-params&quot;&gt;&lt;ul&gt;
 * &lt;li&gt;&lt;b&gt;&lt;tt&gt;tag&lt;/tt&gt;&lt;/b&gt; : &lt;div class=&quot;sub-desc&quot;&gt;The tag name of the element&lt;/div&gt;&lt;/li&gt;
 * &lt;li&gt;&lt;b&gt;&lt;tt&gt;children&lt;/tt&gt;&lt;/b&gt; : or &lt;tt&gt;cn&lt;/tt&gt;&lt;div class=&quot;sub-desc&quot;&gt;An array of the
 * same kind of element definition objects to be created and appended. These can be nested
 * as deep as you want.&lt;/div&gt;&lt;/li&gt;
 * &lt;li&gt;&lt;b&gt;&lt;tt&gt;cls&lt;/tt&gt;&lt;/b&gt; : &lt;div class=&quot;sub-desc&quot;&gt;The class attribute of the element.
 * This will end up being either the &quot;class&quot; attribute on a HTML fragment or className
 * for a DOM node, depending on whether DomHelper is using fragments or DOM.&lt;/div&gt;&lt;/li&gt;
 * &lt;li&gt;&lt;b&gt;&lt;tt&gt;html&lt;/tt&gt;&lt;/b&gt; : &lt;div class=&quot;sub-desc&quot;&gt;The innerHTML for the element&lt;/div&gt;&lt;/li&gt;
 * &lt;/ul&gt;&lt;/div&gt;&lt;/p&gt;
 *
 * &lt;p&gt;&lt;b&gt;&lt;u&gt;Insertion methods&lt;/u&gt;&lt;/b&gt;&lt;/p&gt;
 * &lt;p&gt;Commonly used insertion methods:
 * &lt;div class=&quot;mdetail-params&quot;&gt;&lt;ul&gt;
 * &lt;li&gt;&lt;b&gt;&lt;tt&gt;{@link #append}&lt;/tt&gt;&lt;/b&gt; : &lt;div class=&quot;sub-desc&quot;&gt;&lt;/div&gt;&lt;/li&gt;
 * &lt;li&gt;&lt;b&gt;&lt;tt&gt;{@link #insertBefore}&lt;/tt&gt;&lt;/b&gt; : &lt;div class=&quot;sub-desc&quot;&gt;&lt;/div&gt;&lt;/li&gt;
 * &lt;li&gt;&lt;b&gt;&lt;tt&gt;{@link #insertAfter}&lt;/tt&gt;&lt;/b&gt; : &lt;div class=&quot;sub-desc&quot;&gt;&lt;/div&gt;&lt;/li&gt;
 * &lt;li&gt;&lt;b&gt;&lt;tt&gt;{@link #overwrite}&lt;/tt&gt;&lt;/b&gt; : &lt;div class=&quot;sub-desc&quot;&gt;&lt;/div&gt;&lt;/li&gt;
 * &lt;li&gt;&lt;b&gt;&lt;tt&gt;{@link #createTemplate}&lt;/tt&gt;&lt;/b&gt; : &lt;div class=&quot;sub-desc&quot;&gt;&lt;/div&gt;&lt;/li&gt;
 * &lt;li&gt;&lt;b&gt;&lt;tt&gt;{@link #insertHtml}&lt;/tt&gt;&lt;/b&gt; : &lt;div class=&quot;sub-desc&quot;&gt;&lt;/div&gt;&lt;/li&gt;
 * &lt;/ul&gt;&lt;/div&gt;&lt;/p&gt;
 *
 * &lt;p&gt;&lt;b&gt;&lt;u&gt;Example&lt;/u&gt;&lt;/b&gt;&lt;/p&gt;
 * &lt;p&gt;This is an example, where an unordered list with 3 children items is appended to an existing
 * element with id &lt;tt&gt;'my-div'&lt;/tt&gt;:&lt;br&gt;
 &lt;pre&gt;&lt;code&gt;
var dh = Ext.DomHelper; // create shorthand alias
// specification object
var spec = {
    id: 'my-ul',
    tag: 'ul',
    cls: 'my-list',
    // append children after creating
    children: [     // may also specify 'cn' instead of 'children'
        {tag: 'li', id: 'item0', html: 'List Item 0'},
        {tag: 'li', id: 'item1', html: 'List Item 1'},
        {tag: 'li', id: 'item2', html: 'List Item 2'}
    ]
};
var list = dh.append(
    'my-div', // the context element 'my-div' can either be the id or the actual node
    spec      // the specification object
);
 &lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;
 * &lt;p&gt;Element creation specification parameters in this class may also be passed as an Array of
 * specification objects. This can be used to insert multiple sibling nodes into an existing
 * container very efficiently. For example, to add more list items to the example above:&lt;pre&gt;&lt;code&gt;
dh.append('my-ul', [
    {tag: 'li', id: 'item3', html: 'List Item 3'},
    {tag: 'li', id: 'item4', html: 'List Item 4'}
]);
 * &lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;
 *
 * &lt;p&gt;&lt;b&gt;&lt;u&gt;Templating&lt;/u&gt;&lt;/b&gt;&lt;/p&gt;
 * &lt;p&gt;The real power is in the built-in templating. Instead of creating or appending any elements,
 * &lt;tt&gt;{@link #createTemplate}&lt;/tt&gt; returns a Template object which can be used over and over to
 * insert new elements. Revisiting the example above, we could utilize templating this time:
 * &lt;pre&gt;&lt;code&gt;
// create the node
var list = dh.append('my-div', {tag: 'ul', cls: 'my-list'});
// get template
var tpl = dh.createTemplate({tag: 'li', id: 'item{0}', html: 'List Item {0}'});

for(var i = 0; i &lt; 5, i++){
    tpl.append(list, [i]); // use template to append to the actual node
}
 * &lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;
 * &lt;p&gt;An example using a template:&lt;pre&gt;&lt;code&gt;
var html = '&lt;a id=&quot;{0}&quot; href=&quot;{1}&quot; class=&quot;nav&quot;&gt;{2}&lt;/a&gt;';

var tpl = new Ext.DomHelper.createTemplate(html);
tpl.append('blog-roll', ['link1', 'http://www.jackslocum.com/', &quot;Jack&amp;#39;s Site&quot;]);
tpl.append('blog-roll', ['link2', 'http://www.dustindiaz.com/', &quot;Dustin&amp;#39;s Site&quot;]);
 * &lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;
 *
 * &lt;p&gt;The same example using named parameters:&lt;pre&gt;&lt;code&gt;
var html = '&lt;a id=&quot;{id}&quot; href=&quot;{url}&quot; class=&quot;nav&quot;&gt;{text}&lt;/a&gt;';

var tpl = new Ext.DomHelper.createTemplate(html);
tpl.append('blog-roll', {
    id: 'link1',
    url: 'http://www.jackslocum.com/',
    text: &quot;Jack&amp;#39;s Site&quot;
});
tpl.append('blog-roll', {
    id: 'link2',
    url: 'http://www.dustindiaz.com/',
    text: &quot;Dustin&amp;#39;s Site&quot;
});
 * &lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;
 *
 * &lt;p&gt;&lt;b&gt;&lt;u&gt;Compiling Templates&lt;/u&gt;&lt;/b&gt;&lt;/p&gt;
 * &lt;p&gt;Templates are applied using regular expressions. The performance is great, but if
 * you are adding a bunch of DOM elements using the same template, you can increase
 * performance even further by {@link Ext.Template#compile &quot;compiling&quot;} the template.
 * The way &quot;{@link Ext.Template#compile compile()}&quot; works is the template is parsed and
 * broken up at the different variable points and a dynamic function is created and eval'ed.
 * The generated function performs string concatenation of these parts and the passed
 * variables instead of using regular expressions.
 * &lt;pre&gt;&lt;code&gt;
var html = '&lt;a id=&quot;{id}&quot; href=&quot;{url}&quot; class=&quot;nav&quot;&gt;{text}&lt;/a&gt;';

var tpl = new Ext.DomHelper.createTemplate(html);
tpl.compile();

//... use template like normal
 * &lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;
 *
 * &lt;p&gt;&lt;b&gt;&lt;u&gt;Performance Boost&lt;/u&gt;&lt;/b&gt;&lt;/p&gt;
 * &lt;p&gt;DomHelper will transparently create HTML fragments when it can. Using HTML fragments instead
 * of DOM can significantly boost performance.&lt;/p&gt;
 * &lt;p&gt;Element creation specification parameters may also be strings. If {@link #useDom} is &lt;tt&gt;false&lt;/tt&gt;,
 * then the string is used as innerHTML. If {@link #useDom} is &lt;tt&gt;true&lt;/tt&gt;, a string specification
 * results in the creation of a text node. Usage:&lt;/p&gt;
 * &lt;pre&gt;&lt;code&gt;
Ext.DomHelper.useDom = true; // force it to use DOM; reduces performance
 * &lt;/code&gt;&lt;/pre&gt;
 * @singleton
 */
Ext.DomHelper = function(){
    var tempTableEl = null,
        emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i,
        tableRe = /^table|tbody|tr|td$/i,
        confRe = /tag|children|cn|html$/i,
        tableElRe = /td|tr|tbody/i,
        cssRe = /([a-z0-9-]+)\s*:\s*([^;\s]+(?:\s*[^;\s]+)*);?/gi,
        endRe = /end/i,
        pub,
        // kill repeat to save bytes
        afterbegin = 'afterbegin',
        afterend = 'afterend',
        beforebegin = 'beforebegin',
        beforeend = 'beforeend',
        ts = '&lt;table&gt;',
        te = '&lt;/table&gt;',
        tbs = ts+'&lt;tbody&gt;',
        tbe = '&lt;/tbody&gt;'+te,
        trs = tbs + '&lt;tr&gt;',
        tre = '&lt;/tr&gt;'+tbe;

    // private
    function doInsert(el, o, returnElement, pos, sibling, append){
        var newNode = pub.insertHtml(pos, Ext.getDom(el), createHtml(o));
        return returnElement ? Ext.get(newNode, true) : newNode;
    }

    // build as innerHTML where available
    function createHtml(o){
        var b = '',
            attr,
            val,
            key,
            cn;

        if(typeof o == &quot;string&quot;){
            b = o;
        } else if (Ext.isArray(o)) {
            for (var i=0; i &lt; o.length; i++) {
                if(o[i]) {
                    b += createHtml(o[i]);
                }
            };
        } else {
            b += '&lt;' + (o.tag = o.tag || 'div');
            for (attr in o) {
                val = o[attr];
                if(!confRe.test(attr)){
                    if (typeof val == &quot;object&quot;) {
                        b += ' ' + attr + '=&quot;';
                        for (key in val) {
                            b += key + ':' + val[key] + ';';
                        };
                        b += '&quot;';
                    }else{
                        b += ' ' + ({cls : 'class', htmlFor : 'for'}[attr] || attr) + '=&quot;' + val + '&quot;';
                    }
                }
            };
            // Now either just close the tag or try to add children and close the tag.
            if (emptyTags.test(o.tag)) {
                b += '/&gt;';
            } else {
                b += '&gt;';
                if ((cn = o.children || o.cn)) {
                    b += createHtml(cn);
                } else if(o.html){
                    b += o.html;
                }
                b += '&lt;/' + o.tag + '&gt;';
            }
        }
        return b;
    }

    function ieTable(depth, s, h, e){
        tempTableEl.innerHTML = [s, h, e].join('');
        var i = -1,
            el = tempTableEl,
            ns;
        while(++i &lt; depth){
            el = el.firstChild;
        }
//      If the result is multiple siblings, then encapsulate them into one fragment.
        if(ns = el.nextSibling){
            var df = document.createDocumentFragment();
            while(el){
                ns = el.nextSibling;
                df.appendChild(el);
                el = ns;
            }
            el = df;
        }
        return el;
    }

<span id='global-method-insertIntoTable'>    /**
</span>     * @ignore
     * Nasty code for IE's broken table implementation
     */
    function insertIntoTable(tag, where, el, html) {
        var node,
            before;

        tempTableEl = tempTableEl || document.createElement('div');

        if(tag == 'td' &amp;&amp; (where == afterbegin || where == beforeend) ||
           !tableElRe.test(tag) &amp;&amp; (where == beforebegin || where == afterend)) {
            return;
        }
        before = where == beforebegin ? el :
                 where == afterend ? el.nextSibling :
                 where == afterbegin ? el.firstChild : null;

        if (where == beforebegin || where == afterend) {
            el = el.parentNode;
        }

        if (tag == 'td' || (tag == 'tr' &amp;&amp; (where == beforeend || where == afterbegin))) {
            node = ieTable(4, trs, html, tre);
        } else if ((tag == 'tbody' &amp;&amp; (where == beforeend || where == afterbegin)) ||
                   (tag == 'tr' &amp;&amp; (where == beforebegin || where == afterend))) {
            node = ieTable(3, tbs, html, tbe);
        } else {
            node = ieTable(2, ts, html, te);
        }
        el.insertBefore(node, before);
        return node;
    }

<span id='global-method-createContextualFragment'>    /**
</span>     * @ignore
     * Fix for IE9 createContextualFragment missing method
     */   
    function createContextualFragment(html){
        var div = document.createElement(&quot;div&quot;),
            fragment = document.createDocumentFragment(),
            i = 0,
            length, childNodes;
        
        div.innerHTML = html;
        childNodes = div.childNodes;
        length = childNodes.length;
        
        for (; i &lt; length; i++) {
            fragment.appendChild(childNodes[i].cloneNode(true));
        }
        
        return fragment;
    }
    
    pub = {
<span id='Ext-DomHelper-method-markup'>        /**
</span>         * Returns the markup for the passed Element(s) config.
         * @param {Object} o The DOM object spec (and children)
         * @return {String}
         */
        markup : function(o){
            return createHtml(o);
        },

<span id='Ext-DomHelper-method-applyStyles'>        /**
</span>         * Applies a style specification to an element.
         * @param {String/HTMLElement} el The element to apply styles to
         * @param {String/Object/Function} styles A style specification string e.g. 'width:100px', or object in the form {width:'100px'}, or
         * a function which returns such a specification.
         */
        applyStyles : function(el, styles){
            if (styles) {
                var matches;

                el = Ext.fly(el);
                if (typeof styles == &quot;function&quot;) {
                    styles = styles.call();
                }
                if (typeof styles == &quot;string&quot;) {
<span id='Ext-DomHelper-property-lastIndex'>                    /**
</span>                     * Since we're using the g flag on the regex, we need to set the lastIndex.
                     * This automatically happens on some implementations, but not others, see:
                     * http://stackoverflow.com/questions/2645273/javascript-regular-expression-literal-persists-between-function-calls
                     * http://blog.stevenlevithan.com/archives/fixing-javascript-regexp
                     */
                    cssRe.lastIndex = 0;
                    while ((matches = cssRe.exec(styles))) {
                        el.setStyle(matches[1], matches[2]);
                    }
                } else if (typeof styles == &quot;object&quot;) {
                    el.setStyle(styles);
                }
            }
        },
<span id='Ext-DomHelper-method-insertHtml'>        /**
</span>         * Inserts an HTML fragment into the DOM.
         * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
         * @param {HTMLElement} el The context element
         * @param {String} html The HTML fragment
         * @return {HTMLElement} The new node
         */
        insertHtml : function(where, el, html){
            var hash = {},
                hashVal,
                range,
                rangeEl,
                setStart,
                frag,
                rs;

            where = where.toLowerCase();
            // add these here because they are used in both branches of the condition.
            hash[beforebegin] = ['BeforeBegin', 'previousSibling'];
            hash[afterend] = ['AfterEnd', 'nextSibling'];

            if (el.insertAdjacentHTML) {
                if(tableRe.test(el.tagName) &amp;&amp; (rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html))){
                    return rs;
                }
                // add these two to the hash.
                hash[afterbegin] = ['AfterBegin', 'firstChild'];
                hash[beforeend] = ['BeforeEnd', 'lastChild'];
                if ((hashVal = hash[where])) {
                    el.insertAdjacentHTML(hashVal[0], html);
                    return el[hashVal[1]];
                }
            } else {
                range = el.ownerDocument.createRange();
                setStart = 'setStart' + (endRe.test(where) ? 'After' : 'Before');
                if (hash[where]) {
                    range[setStart](el);
                    if (!range.createContextualFragment) {
                        frag = createContextualFragment(html);
                    }
                    else {
                        frag = range.createContextualFragment(html);
                    }
                    el.parentNode.insertBefore(frag, where == beforebegin ? el : el.nextSibling);
                    return el[(where == beforebegin ? 'previous' : 'next') + 'Sibling'];
                } else {
                    rangeEl = (where == afterbegin ? 'first' : 'last') + 'Child';
                    if (el.firstChild) {
                        range[setStart](el[rangeEl]);
                        if (!range.createContextualFragment) {
                            frag = createContextualFragment(html);
                        }
                        else {
                            frag = range.createContextualFragment(html);
                        }
                        if(where == afterbegin){
                            el.insertBefore(frag, el.firstChild);
                        }else{
                            el.appendChild(frag);
                        }
                    } else {
                        el.innerHTML = html;
                    }
                    return el[rangeEl];
                }
            }
            throw 'Illegal insertion point -&gt; &quot;' + where + '&quot;';
        },

<span id='Ext-DomHelper-method-insertBefore'>        /**
</span>         * Creates new DOM element(s) and inserts them before el.
         * @param {Mixed} el The context element
         * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
         * @param {Boolean} returnElement (optional) true to return a Ext.Element
         * @return {HTMLElement/Ext.Element} The new node
         */
        insertBefore : function(el, o, returnElement){
            return doInsert(el, o, returnElement, beforebegin);
        },

<span id='Ext-DomHelper-method-insertAfter'>        /**
</span>         * Creates new DOM element(s) and inserts them after el.
         * @param {Mixed} el The context element
         * @param {Object} o The DOM object spec (and children)
         * @param {Boolean} returnElement (optional) true to return a Ext.Element
         * @return {HTMLElement/Ext.Element} The new node
         */
        insertAfter : function(el, o, returnElement){
            return doInsert(el, o, returnElement, afterend, 'nextSibling');
        },

<span id='Ext-DomHelper-method-insertFirst'>        /**
</span>         * Creates new DOM element(s) and inserts them as the first child of el.
         * @param {Mixed} el The context element
         * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
         * @param {Boolean} returnElement (optional) true to return a Ext.Element
         * @return {HTMLElement/Ext.Element} The new node
         */
        insertFirst : function(el, o, returnElement){
            return doInsert(el, o, returnElement, afterbegin, 'firstChild');
        },

<span id='Ext-DomHelper-method-append'>        /**
</span>         * Creates new DOM element(s) and appends them to el.
         * @param {Mixed} el The context element
         * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
         * @param {Boolean} returnElement (optional) true to return a Ext.Element
         * @return {HTMLElement/Ext.Element} The new node
         */
        append : function(el, o, returnElement){
            return doInsert(el, o, returnElement, beforeend, '', true);
        },

<span id='Ext-DomHelper-method-overwrite'>        /**
</span>         * Creates new DOM element(s) and overwrites the contents of el with them.
         * @param {Mixed} el The context element
         * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
         * @param {Boolean} returnElement (optional) true to return a Ext.Element
         * @return {HTMLElement/Ext.Element} The new node
         */
        overwrite : function(el, o, returnElement){
            el = Ext.getDom(el);
            el.innerHTML = createHtml(o);
            return returnElement ? Ext.get(el.firstChild) : el.firstChild;
        },

        createHtml : createHtml
    };
    return pub;
}();
</pre>
</body>
</html>
